%option noyywrap noinput yylineno warn reentrant bison-bridge bison-locations
%option prefix="vxp_"
%option extra-type="struct vxpState*"

/* Note: Only ASCII characters are supported for now. */

%{
	#ifdef PHPEXT
		#include "php.h"
		#include "php_vxp.h"
	#endif

    #include "tokens.h"
#ifndef PHPEXT
    #include "errors.h"
#endif

    #include "vxp-state.h"

    int isAxis(enum yytokentype token);
    int isOperator(enum yytokentype token);

#define YY_DECL enum yytokentype vxp_lex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, void* yyscanner)


    #define YY_USER_ACTION              \
    {                                   \
        yylloc_param->first_line=1;     \
        yylloc_param->first_column=yylloc_param->last_column;     \
        yylloc_param->last_column+=yyleng;    \
        yylloc_param->last_line =1;    \
    }

    #define RETURN(x)                           \
    {                                           \
        struct vxpState* state=vxp_get_extra(yyscanner); \
        if (x==NCNAME)                          \
            yylval_param->s=memNewStringEx(yytext,yyleng,state->mem); \
        else                                    \
        if (x==LITERAL)                         \
            yylval_param->s=memNewStringEx(yytext+1,yyleng-2,state->mem); \
        else                                    \
            yylval_param->t=x;                  \
                                                \
        state->lastToken=x;                            \
                                                \
        return x;                               \
    }
%}

digits    [0-9]+
number    ({digits}(\.{digits}?)?|\.{digits})
literalQQ (\"[^\"]*\")
literalQ  (\'[^\']*\')
ws        (\x20|\x9|\xD|\xA)+
eol       [\n\r]

 /* XML Name Start Char */
xnsc    [A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\xFF]
 /* XML Name Char */
xnc     {xnsc}|[\-\.0-9\xB7]
 /* XML Name */
xname   {xnsc}{xnc}*

%%

 /* Line/Column tracking */
{eol}                   { yylloc_param->first_column=yylloc_param->last_column=0; YY_USER_ACTION; }

 /* Symbols */
"@"                     { RETURN(AT_SIGN); }
":"                     { RETURN(COLON); }
","                     { RETURN(COMMA); }
"$"                     { RETURN(DOLLAR_SIGN); }
"."                     { RETURN(DOT); }
".."                    { RETURN(DOT_DOT); }
"::"                    { RETURN(DOUBLE_COLON); }
"["                     { RETURN(LEFT_BRACKET); }
"("                     { RETURN(LEFT_PAREN); }
"]"                     { RETURN(RIGHT_BRACKET); }
")"                     { RETURN(RIGHT_PAREN); }

 /* Operators */
"and"                   {
                          enum yytokentype lastToken=vxp_get_extra(yyscanner)->lastToken;
                          if ((lastToken) && !(isAxis(lastToken) || isOperator(lastToken)))
                              RETURN(AND)

                          RETURN(NCNAME)
                        }
"div"                   {
                          enum yytokentype lastToken=vxp_get_extra(yyscanner)->lastToken;
                          if ((lastToken) && !(isAxis(lastToken) || isOperator(lastToken)))
                              RETURN(DIV)

                          RETURN(NCNAME)
                        }
"="                     { RETURN(EQUALS); }
">"                     { RETURN(GREATER_THAN); }
">="                    { RETURN(GREATER_THAN_OR_EQUALS); }
"<"                     { RETURN(LESS_THAN); }
"<="                    { RETURN(LESS_THAN_OR_EQUALS); }
"-"                     { RETURN(MINUS); }
"mod"                   { 
                          enum yytokentype lastToken=vxp_get_extra(yyscanner)->lastToken;
                          if ((lastToken) && !(isAxis(lastToken) || isOperator(lastToken)))
                              RETURN(MOD)

                          RETURN(NCNAME)
                        }
"!="                    { RETURN(NOT_EQUALS); }
"or"                    { 
                          enum yytokentype lastToken=vxp_get_extra(yyscanner)->lastToken;
                          if ((lastToken) && !(isAxis(lastToken) || isOperator(lastToken)))
                              RETURN(OR)

                          RETURN(NCNAME)
                        }
"|"                     { RETURN(PIPE); }
"+"                     { RETURN(PLUS); }
"/"                     { RETURN(SLASH); }
"//"                    { RETURN(SLASH_SLASH); }
 /* TODO: See Section 3.7 of the XPath 1.0 Standard */
"*"                     { 
                          enum yytokentype lastToken=vxp_get_extra(yyscanner)->lastToken;
                          if ((lastToken) && !(isAxis(lastToken) || isOperator(lastToken)))
                              RETURN(MULTIPLY)

                          RETURN(ANY);
                        }

 /* Axis */
 /* Section 3.7, Lexical Structure from XPath 1.0 Grammar:
    "If the two characters following an NCName (possibly after intervening ExprWhitespace) are '::',
    then the token must be recognized as an AxisName."

    This implies that in all other cases, such as this XPath query: 'ancestor', the axis names
    must be recognized as NCNames, not as AxisNames.
 */
"ancestor"              { RETURN(NCNAME); }
"ancestor"{ws}?"::"     { unput(':');unput(':'); RETURN(ANCESTOR); }

"ancestor-or-self"      { RETURN(NCNAME); }
"ancestor-or-self"{ws}?"::"     { unput(':');unput(':'); RETURN(ANCESTOR_OR_SELF); }

"attribute"             { RETURN(NCNAME); }
"attribute"{ws}?"::"    { unput(':');unput(':'); RETURN(ATTRIBUTE); }

"child"                 { RETURN(NCNAME); }
"child"{ws}?"::"        { unput(':');unput(':'); RETURN(CHILD); }

"descendant"            { RETURN(NCNAME); }
"descendant"{ws}?"::"   { unput(':');unput(':'); RETURN(DESCENDANT); }

"descendant-or-self"    { RETURN(NCNAME); }
"descendant-or-self"{ws}?"::"   { unput(':');unput(':'); RETURN(DESCENDANT_OR_SELF); }

"following"             { RETURN(NCNAME); }
"following"{ws}?"::"    { unput(':');unput(':'); RETURN(FOLLOWING); }

"following-sibling"     { RETURN(NCNAME); }
"following-sibling"{ws}?"::"    { unput(':');unput(':'); RETURN(FOLLOWING_SIBLING); }

"namespace"             { RETURN(NCNAME); }
"namespace"{ws}?"::"    { unput(':');unput(':'); RETURN(NAMESPACE); }

"parent"                { RETURN(NCNAME); }
"parent"{ws}?"::"       { unput(':');unput(':'); RETURN(PARENT); }

"preceding"             { RETURN(NCNAME); }
"preceding"{ws}?"::"    { unput(':');unput(':'); RETURN(PRECEDING); }

"preceding-sibling"     { RETURN(NCNAME); }
"preceding-sibling"{ws}?"::"    { unput(':');unput(':'); RETURN(PRECEDING_SIBLING); }

"self"                  { RETURN(NCNAME); }
"self"{ws}?"::"         { unput(':');unput(':'); RETURN(SELF); }

 /* NodeType */
 /* Section 3.7, Lexical Structure from XPath 1.0 Grammar:
    "If the character following an NCName (possibly after intervening ExprWhitespace) is '(',
    then the token must be recognized as a NodeType or a FunctionName.

    This implies that in all other cases, such as this XPath query: 'comment', the node type
    names must be recognized as NCNames, not as NodeTypes or FunctionNames.
 */
"comment"               { RETURN(NCNAME); }
"comment"{ws}?"("       { unput('('); RETURN(COMMENT); }
"node"                  { RETURN(NCNAME); }
"node"{ws}?"("          { unput('('); RETURN(NODE); }
"processing-instruction" {RETURN(NCNAME); }
"processing-instruction"{ws}?"(" { 
                            unput('('); 
                            RETURN(PROCESSING_INSTRUCTION); 
                        }
"text"                  { RETURN(NCNAME); }
"text"{ws}?"("          { unput('('); RETURN(TEXT); }

{literalQ}              { 
                          struct vxpState* state=vxp_get_extra(yyscanner);
                          state->quoteType=SINGLE_QUOTES;
                          RETURN(LITERAL);
                        }
{literalQQ}             {
                          struct vxpState* state=vxp_get_extra(yyscanner);
                          state->quoteType=DOUBLE_QUOTES;
                          RETURN(LITERAL);
                        }
{number}                { RETURN(NUMBER); }
{ws}                    { /* ignore */ }
{xname}                 { RETURN(NCNAME); }
.						{ 
                          struct vxpState* state=vxp_get_extra(yyscanner);
                          state->errorKind=VXP_ERROR_BAD_CHAR;
                          badcharerror(yytext[0],yylloc_param->first_line,yylloc_param->first_column,state->mem);
                        }
%%

int isOperator(enum yytokentype token)
{
    switch(token)
    {
        case AND:
        case DIV:
        case EQUALS:
        case GREATER_THAN:
        case GREATER_THAN_OR_EQUALS:
        case LESS_THAN:
        case LESS_THAN_OR_EQUALS:
        case MINUS:
        case MULTIPLY:
        case MOD:
        case NOT_EQUALS:
        case OR:
        case PIPE:
        case PLUS:
        case SLASH:
        case SLASH_SLASH:
                        return 1;
        default:
                        return 0;
    }

    return 0;
}

int isAxis(enum yytokentype token)
{
      switch(token)
      {
          case AT_SIGN:
          case COMMA:
          case DOUBLE_COLON:
          case LEFT_PAREN:
          case LEFT_BRACKET:
                            return 1;
          default: 
                            return 0;
      }

      return 0;
}



